//===----------------------------------------------------------------------===//
//
// This file implement the functions to mangle LLVM types
//
//===----------------------------------------------------------------------===//

#include "reflow/Support/Mangle.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/CodeGen/ValueTypes.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"

using namespace llvm;
/// Returns a stable mangling for the type specified for use in the name
/// mangling scheme used by 'any' types in intrinsic signatures.  The mangling
/// of named types is simply their name.  Manglings for unnamed types consist
/// of a prefix ('p' for pointers, 'a' for arrays, 'f_' for functions)
/// combined with the mangling of their component types.  A vararg function
/// type will have a suffix of 'vararg'.  Since function types can contain
/// other function types, we close a function type mangling with suffix 'f'
/// which can't be confused with it's prefix.  This ensures we don't have
/// collisions between two unrelated function types. Otherwise, you might
/// parse ffXX as f(fXX) or f(fX)X.  (X is a placeholder for any other type.)
/// Manglings of integers, floats, and vectors ('i', 'f', and 'v' prefix in most
/// cases) fall back to the MVT codepath, where they could be mangled to
/// 'x86mmx', for example; matching on derived types is not sufficient to mangle
/// everything.
std::string llvm::getMangledTypeStr(Type *Ty) {
  std::string Result;
  if (PointerType *PTyp = dyn_cast<PointerType>(Ty)) {
    Result += "p" + llvm::utostr(PTyp->getAddressSpace()) +
              getMangledTypeStr(PTyp->getElementType());
  } else if (ArrayType *ATyp = dyn_cast<ArrayType>(Ty)) {
    Result += "a" + llvm::utostr(ATyp->getNumElements()) +
              getMangledTypeStr(ATyp->getElementType());
  } else if (StructType *STyp = dyn_cast<StructType>(Ty)) {
    if (STyp->isLiteral()) {
      Result += "L_";
      for (auto *ET : STyp->elements())
        Result += getMangledTypeStr(ET);
      if (STyp->isPacked())
        Result += "packed";
      // Ensure nested literal types are distinguishable.
      Result += "L";
    } else
      Result += STyp->getName();
  } else if (FunctionType *FT = dyn_cast<FunctionType>(Ty)) {
    Result += "f_" + getMangledTypeStr(FT->getReturnType());
    for (size_t i = 0; i < FT->getNumParams(); i++)
      Result += getMangledTypeStr(FT->getParamType(i));
    if (FT->isVarArg())
      Result += "vararg";
    // Ensure nested function types are distinguishable.
    Result += "f";
  } else if (isa<VectorType>(Ty))
    Result += "v" + utostr(Ty->getVectorNumElements()) +
              getMangledTypeStr(Ty->getVectorElementType());
  else if (Ty)
    Result += EVT::getEVT(Ty).getEVTString();
  return Result;
}

std::string llvm::getMangledTypeStr(ArrayRef<Type *> Tys) {
  std::string Name;
  for (auto *Ty: Tys) {
    Name += "." + getMangledTypeStr(Ty);
  }
  return Name;
}

/// Check the demangled function name
std::string llvm::getDemangleName(const Function *Fn) {
  auto NameRef = Fn->getName();

  if (!NameRef.startswith("_Z"))
    return NameRef;

  if (Fn->hasFnAttribute("fpga.demangled.name"))
    return Fn->getFnAttribute("fpga.demangled.name").getValueAsString();
  std::string Name = NameRef;

  int Status = 0;
  if (char *Demangled =
          itaniumDemangle(Name.c_str(), nullptr, nullptr, &Status)) {
    if (Status == 0)
      Name = Demangled;
    std::free(Demangled);
    return Name;
  }

  return Name;
}

std::string llvm::getDemangleName(StringRef NameRef) {
  if (!NameRef.startswith("_Z"))
    return NameRef;

  std::string Name = NameRef;

  int Status = 0;
  if (char *Demangled =
          itaniumDemangle(Name.c_str(), nullptr, nullptr, &Status)) {
    if (Status == 0)
      Name = Demangled;
    std::free(Demangled);
    return Name;
  }

  return Name;
}

// c++ demangling API
std::string llvm::getSubStrDemangleName(std::string input, bool no_param)
{
    // for GCC only: first must be _ and second must by A-Z
    if (input.size() <= 2) return input;
    if (input[0] != '_') return input;
    if ((input[1] > 'Z' || input[1] < 'A') && input[1] != '_') return input;

    static llvm::ItaniumPartialDemangler Context;

    // not a mangling name.
    if (Context.partialDemangle(input.c_str()))
        return input;

    if (!Context.isFunction() || !no_param) {
        std::unique_ptr<char, void(*)(void*)> res {
            Context.finishDemangle(nullptr, nullptr), std::free };
        return res.get();
    }

    // return function name without param
    std::unique_ptr<char, void(*)(void*)> res {
        Context.getFunctionName(nullptr, nullptr), std::free };
    return res.get();
}

/// Make adaptive to old libiberty cplus_demangle API:
///   in 3.9 flow, following string will appear, just catch the first string before
///   the first '.', compatable with old demangle API.
///   "_Z19find_Btranspose_newILi3EEvPA2048_7ap_uintILi128EEPS1_ib.auFgemmTop.1"
std::string llvm::getDemangleName(std::string input, bool no_param) {
    SmallVector<StringRef, 5> StrVec;
    StringRef(input).split(StrVec, ".");
    if (StrVec.empty())
        return input;

    // Assume that the padding string always added at the end of the mangled name.
    std::string DeStr = getSubStrDemangleName(StrVec[0].str(), no_param);
    if (DeStr != StrVec[0].str())
        return DeStr;
    // And if the first section is failed to demangle, then pass the total string.
    // llvm.dbg.value
    return getSubStrDemangleName(input, no_param);
}

std::string llvm::getDemangleName(const Value *V) {
  if (!V)
    return "nullptr";
  if (V->hasName())
    return getDemangleName(V->getName());
  return "<internal>";
}

/// get Type name
std::string llvm::GetTypeString(unsigned SizeInBits) {
  switch (SizeInBits) {
    case 8:
      return "char";
    case 16:
      return "short";
    case 32:
      return "int";
    case 64:
      return "long";
    default:
      break;
  }
  return "int" + utostr(SizeInBits);
}

std::string llvm::GetTypeString(const Type *T, const DataLayout &DL) {
  if (T->isVoidTy())
    return "void";

  if (T->isHalfTy())
    return "half";

  if (T->isFloatTy())
    return "float";

  if (T->isDoubleTy())
    return "double";

  return GetTypeString(DL.getTypeSizeInBits(const_cast<Type*>(T)));
}

std::string llvm::removeTemplate(std::string s)
{
    size_t l = s.find('<');
    while (l != std::string::npos) {
        int match = 0;
        size_t i = l;
        for (; i < s.size(); i++) {
            if (s[i] == '<')
                match++;
            else if (s[i] == '>') {
                match --;
                if (match == 0)
                    break;
            }
        }
        if (i == s.size())
            return s;
        s = s.erase(l , i - l + 1);
        l = s.find('<');
    }
    return s;
}

// Before calling this API, this function must be constructor OR destructor
// 0: means nothing
// 1: constructor name
//-1: destructure name
int llvm::JudgeConDestructorName(Function* F)
{
  if (!F) return 0;
  std::string name = getDemangleName(F->getName(), true);
  std::vector<std::string> tokens;
  char * pch;

  name = removeTemplate(name);
  char* str = new char[name.size()+1];
  strcpy(str, name.c_str());
  pch = strtok(str ,":");
  while (pch != 0) {
      tokens.push_back(std::string(pch));
      pch = strtok (NULL, ":");
  }
  delete[] str;

  if (tokens.size() < 2) return 0;

  std::string className = tokens[tokens.size() - 2];
  std::string funcName = tokens[tokens.size() - 1];

  size_t pos = funcName.find_last_of(".");
  if (pos != std::string::npos) {
      size_t i = pos + 1;
      while ( i++ < funcName.size() && isdigit(funcName[i]));
      if (i == funcName.size())
         funcName = funcName.substr(0, pos);
  }
  if (className == funcName) {
      // constructor name
      return 1;
  } else if (funcName[0] == '~' && className == funcName.substr(1)) {
      // destructor name
      return -1;
  }
  return 0;
}


// return value:
// 1. true: is constructor OR destructor; false: not constructor and destructor
// 2. true: is constructor; false: is destructor
std::pair<bool, bool> llvm::isConDestructor(Function* F)
{
  std::pair<bool, bool> result;
  result.first  = false;
  result.second = false;

  if (!F)
    return result;
  std::string FName = F->getName().str();
  if (FName.empty())
    return result;

  // for GCC only: first must be _ and second must by A-Z
  if (FName.size() <= 2)
    return result;
  if (FName[0] != '_')
    return result;
  if ((FName[1] > 'Z' || FName[1] < 'A') && FName[1] != '_')
    return result;
  // Remove ".1/.2" names due to function clone
  auto position = FName.find(".");
  if (position != std::string::npos) {
    if (position == 0)
      return result;
    else
      FName = FName.substr(0, position - 1);
  }

  // start to judge constructor/destructor
  static llvm::ItaniumPartialDemangler Context;
  // not a mangling name
  if (Context.partialDemangle(FName.c_str()))
    return result;
  if (!Context.isFunction())
    return result;
  // return isCtorOrDtor
  if (Context.isCtorOrDtor()) {
    result.first = true;
    int flag = JudgeConDestructorName(F);
    if (flag == 1)
      result.second = true;
    else if (flag == -1)
      result.second = false;
    else if (flag == 0)
      assert (0 && "conflict happens when judging constructor/destructor function name!");
    return result;
  }

  // Else, false
  return result;
}

bool llvm::isConstructor(llvm::Function* F)
{
  std::pair<bool, bool> result;
  result = isConDestructor(F);
  if (result.first)
      return result.second;
  return false;
}

bool llvm::isDestructor(llvm::Function* F)
{
  std::pair<bool, bool> result;
  result = isConDestructor(F);
  if (result.first)
      return !(result.second);
  return false;
}



namespace {
struct KeyWordsTable : public StringSet<> {
  KeyWordsTable() : StringSet<>({
#include "VHDL.key"
#include "VLOG.key"
#include "COSIM.key"
  }) {}
};
}

bool llvm::isHDLKeyWord(StringRef Str) {
  static ManagedStatic<KeyWordsTable> TheTable;
  return TheTable->count(Str.lower());
}

StringRef llvm::getAXISSideChannelName(uint8_t ID) {
  switch (ID) {
    case 0: return "data";
    case 1: return "keep";
    case 2: return "strb";
    case 3: return "user";
    case 4: return "last";
    case 5: return "id";
    case 6: return "dest";
    default: assert(0 && "Wrong index for AXIS side channel!");
  }

  return "";
}

std::string llvm::getAXISSideChannelName(StringRef GroupName, uint8_t ID) {
  return GroupName.str() + "_V_" + getAXISSideChannelName(ID).str() + "_V";
}
